package ro.codetwisters.requestor;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import ro.codetwisters.requestor.gui.MainForm;
import ro.codetwisters.requestor.json.JSONArray;
import ro.codetwisters.requestor.json.JSONException;
import ro.codetwisters.requestor.json.JSONObject;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.io.*;

/**
 * Created by IntelliJ IDEA.
 * User: codetwister
 * Date: 7/1/11
 * Time: 9:27 PM
 * To change this template use File | Settings | File Templates.
 */
public class Application implements ActionListener, FocusListener {

    public static final int BUFFER_SIZE = 1024;

    public static final String ACTION_GET = "GET";
    public static final String ACTION_POST = "POST";
    public static final String ACTION_HEAD = "HEAD";
    public static final String ACTION_PUT = "PUT";
    public static final String ACTION_DELETE = "DELETE";
    public static final String ACTION_ADD_HEADER = "Add Header";
    public static final String ACTION_CLEAR = "Clear";
    public static final String ACTION_REMOVE_SELECTED = "Remove Selected";
    public static final String ACTION_SAVE_CONTENT = "Save Content";
    public static final String ACTION_SAVE_REQUEST = "Save Request";

    private String requestResult;

    private MainForm form;
    private JFrame frame;
    private DefaultListModel listModel;

    final JFileChooser fc = new JFileChooser();

    public Application(MainForm form, JFrame frame) {
        this.form = form;
        this.frame = frame;
        form.getGETButton().addActionListener(this);
        form.getPOSTButton().addActionListener(this);
        form.getHEADButton().addActionListener(this);
        form.getPUTButton().addActionListener(this);
        form.getDELETEButton().addActionListener(this);
        form.getAddHeaderButton().addActionListener(this);
        form.getRemoveSelectedButton().addActionListener(this);
        form.getClearButton().addActionListener(this);
        form.getSaveContentButton().addActionListener(this);
        form.getSaveRequestButton().addActionListener(this);
        form.getSavedRequestsCombo().addActionListener(this);

        form.getJsonRpcRequestIdField().addFocusListener(this);
        form.getJsonRpcMethodField().addFocusListener(this);
        form.getJsonRpcParamsField().addFocusListener(this);

        listModel = new DefaultListModel();
        form.getHeaderList().setModel(listModel);
        loadSavedRequests();

    }

    public void actionPerformed(ActionEvent e) {
        String a = e.getActionCommand();

        if (a.equals(ACTION_ADD_HEADER)) {
            addHeader();
        } else if (a.equals(ACTION_REMOVE_SELECTED)) {
            removeSelectedHeader();
        } else if (a.equals(ACTION_CLEAR)) {
            clearHeaders();
        } else if (a.equals(ACTION_SAVE_CONTENT)) {
            saveContentToFile();
        } else if (a.equals(ACTION_SAVE_REQUEST)) {
            saveRequest();
        } else if (e.getSource().equals(form.getSavedRequestsCombo())) {
            JComboBox combo = (JComboBox) e.getSource();
            String selectedRequest = (String) combo.getSelectedItem();
            loadSelectedRequest(selectedRequest);
        } else {
            // it must be a request type
            makeRequest(a);
        }
    }

    private void loadSavedRequests() {
        File savedReqDir = new File("saved_requests");
        String[] savedReq = savedReqDir.list();
        DefaultComboBoxModel model = new DefaultComboBoxModel();
        for (String s : savedReq) {
            model.addElement(s.substring(0, s.length()-9));
        }
        form.getSavedRequestsCombo().setModel(model);
    }

    private void loadSelectedRequest(String selectedRequest) {
        File savedReqFile = new File("saved_requests"+File.separator+selectedRequest+".req.json");
        try {
            FileReader fr = new FileReader(savedReqFile);
            char[] buffer = new char[BUFFER_SIZE];
            int count;
            StringBuilder sb = new StringBuilder();
            while ((count = fr.read(buffer)) != -1) {
                sb.append(buffer, 0, count);
            }
            if (sb.length() == 0) {
                // TODO error message
            } else {
                JSONObject reqObj = new JSONObject(sb.toString());
                form.getUrlTextField().setText(reqObj.optString("url"));

                JSONArray headersArr = reqObj.optJSONArray("headers");
                listModel.removeAllElements();
                for (int i = 0; i < headersArr.length(); i++) {
                    JSONObject headerJson = headersArr.optJSONObject(i);
                    MyHttpHeader h = new MyHttpHeader(headerJson);
                    listModel.addElement(h);
                }

                int selectedTab = reqObj.optInt("inputType");
                form.getTabbedPane().setSelectedIndex(selectedTab);
                switch (selectedTab) {
                    case 0:
                        // raw input
                        form.getRawContentTextPane().setText(reqObj.optString("input"));
                        break;
                    case 1:
                        // html form
                        break;
                    case 2:
                        // json-rpc
                        JSONObject jsonRpcInput = reqObj.optJSONObject("input");
                        form.getJsonRpcRawOutput().setText(jsonRpcInput.toString());
                        form.getJsonRpcMethodField().setText(jsonRpcInput.optString("method"));
                        form.getJsonRpcRequestIdField().setText(jsonRpcInput.optString("id"));
                        form.getJsonRpcParamsField().setText(jsonRpcInput.optString("params"));
                        break;
                }

            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        } catch (JSONException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

    }

    private void saveRequest() {
        String s = (String)JOptionPane.showInputDialog(
                frame,
                "Please give a name to your request",
                "Save Request",
                JOptionPane.PLAIN_MESSAGE);

        if ((s != null) && (s.length() > 0)) {
            // gather all data and save it to a file...
            // we will save the data as serialized json
            JSONObject requestObject = new JSONObject();
            try {
                requestObject.put("url", form.getUrlTextField().getText());
                JSONArray headersArr = new JSONArray();
                for (int i = 0; i < listModel.getSize(); i++) {
                    MyHttpHeader h = (MyHttpHeader) listModel.getElementAt(i);
                    headersArr.put(h.getJsonObj());
                }
                requestObject.put("headers", headersArr);
                int selectedTab = form.getTabbedPane().getSelectedIndex();
                requestObject.put("inputType", selectedTab); // 0/1/2 (raw/html/jsonrpc)
                switch (selectedTab) {
                    case 0:
                        // raw input
                        requestObject.put("input", form.getRawContentTextPane().getText());
                        break;
                    case 1:
                        // html form
                        break;
                    case 2:
                        // json-rpc
                        requestObject.put("input", new JSONObject(form.getJsonRpcRawOutput().getText()));
                        break;
                }
                File savedReqFile = new File("saved_requests"+File.separator+s+".req.json");
                if (savedReqFile.exists()) {
                    savedReqFile.delete();
                }
                FileWriter fw = new FileWriter(savedReqFile);
                fw.append(requestObject.toString());
                fw.close();
            } catch (JSONException e) {
                e.printStackTrace();
            } catch (FileNotFoundException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
        loadSavedRequests();
    }

    private void saveContentToFile() {
        int returnVal = fc.showSaveDialog(frame);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            // TODO
            FileWriter fw = null;
            try {
                fw = new FileWriter(file);
                fw.append(requestResult);
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }
        }
    }

    private void clearHeaders() {
        listModel.removeAllElements();
    }

    private void removeSelectedHeader() {
        int i = form.getHeaderList().getSelectedIndex();
        if (i >= 0) {
            listModel.remove(i);
        }
    }

    private void addHeader() {
        listModel.addElement(new MyHttpHeader(form.getHeaderTextField().getText(), form.getHeaderValueTextField().getText()));
    }

    private void makeRequest(String type) {
        HttpClient httpClient = new DefaultHttpClient();
        HttpUriRequest request = getRequest(type);
        try {
            prepareRequest(request);
            HttpResponse response = null;
            response = httpClient.execute(request);
            handleRequestResult(response);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void prepareRequest(HttpUriRequest request) throws UnsupportedEncodingException {
        // add headers
        for (int i = 0; i < listModel.getSize(); i++) {
            MyHttpHeader h = (MyHttpHeader) listModel.getElementAt(i);
            request.addHeader(h.getName(), h.getValue());
        }

        if (request instanceof HttpPost) {
            ((HttpPost) request).setEntity(getHttpEntity());
        } else if (request instanceof HttpPut) {
            ((HttpPut) request).setEntity(getHttpEntity());
        }
    }

    private HttpEntity getHttpEntity() throws UnsupportedEncodingException {
        // we check witch tab is selected
        String reqString = "";
        int selectedTab = form.getTabbedPane().getSelectedIndex();
        switch (selectedTab) {
            case 0:
                // raw input
                reqString = form.getRawContentTextPane().getText();
                break;
            case 1:
                // html form
                break;
            case 2:
                // json-rpc
                reqString = form.getJsonRpcRawOutput().getText();
                break;
        }
        return new ByteArrayEntity(reqString.getBytes());
    }

    private void handleRequestResult(HttpResponse response) throws IOException {
        String resultStr = "HEADERS: \r\n\r\n";

        for (Header header : response.getAllHeaders()) {
            resultStr += header.getName()+": "+header.getValue()+"\r\n";
        }

        resultStr += "\r\n\r\nCONTENT: \r\n\r\n";

        HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream in = entity.getContent();
            char[] buffer = new char[BUFFER_SIZE];
            int count;
            InputStreamReader ir = new InputStreamReader(in);
            StringBuilder sb = new StringBuilder();
            while ((count = ir.read(buffer)) != -1) {
                sb.append(buffer, 0, count);
            }
            if (sb.length() == 0) {
                // set error message
                resultStr += "NO RESPONSE";
            } else {
                requestResult = sb.toString();
                resultStr += requestResult;
            }
        }

        form.getResponseEditorPane().setText(resultStr);
    }

    private HttpUriRequest getRequest(String type) {
        String url = form.getUrlTextField().getText();
        if (type.equals(ACTION_GET)) {
            return new HttpGet(url);
        } else if (type.equals(ACTION_POST)) {
            return new HttpPost(url);
        } else if (type.equals(ACTION_HEAD)) {
            return new HttpHead(url);
        } else if (type.equals(ACTION_PUT)) {
            return new HttpPut(url);
        } else if (type.equals(ACTION_DELETE)) {
            return new HttpDelete(url);
        }
        return null;
    }

    public void focusGained(FocusEvent e) {
        // nothing to do here
    }

    public void focusLost(FocusEvent e) {
        if (e.getSource().equals(form.getJsonRpcMethodField()) ||
                e.getSource().equals(form.getJsonRpcParamsField()) ||
                e.getSource().equals(form.getJsonRpcRequestIdField())) {
            renderJsonRpc();
        }
    }

    private void renderJsonRpc() {
        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("jsonrpc", "2.0");
            jsonObject.put("id", form.getJsonRpcRequestIdField().getText());
            jsonObject.put("method", form.getJsonRpcMethodField().getText());
            jsonObject.put("params", new JSONObject(form.getJsonRpcParamsField().getText()));
        } catch (JSONException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        form.getJsonRpcRawOutput().setText(jsonObject.toString());
    }
}
